"""
Token
"""

from datetime import datetime
from typing import Optional

import jwt
from devtools import debug

from components.exceptions import BusinessException
from config import settings
from db import token_db
from schema.db_schema import TokenDBModel
from schema.view_schema import JWTPayloadViewModel
from utils.converter_utils.time import TimeConverter
from utils.mock_utils import DATETIME


class TokenHandler:
    """ Token 控制器 """

    class Payload:
        """ Payload 控制器 """

        @staticmethod
        def get_subject() -> str:
            """ 获取 JWT 主题 """

            return settings.CONST.PROJECT_NAME

        @staticmethod
        def get_now_time() -> datetime:
            """
            获取当前时间

            Returns:
                datetime: 当前时间
            """

            return DATETIME.now()

        @staticmethod
        def get_expire_time(
            issue_time: datetime,
            token_max_age: Optional[int] = None,
        ) -> datetime:
            """
            获取 JWT Token 过期时间

            Args:
                issue_time (datetime): 签发时间
                token_max_age (Optional[int]): Token 过期天数

            Returns:
                str: JWT Token 过期时间
            """

            if token_max_age is None:
                token_max_age = settings.SECURITY.TOKEN_MAX_AGE

            expire_time: datetime = TimeConverter.get_delta_times(issue_time, days=token_max_age)

            return expire_time

        @classmethod
        def get_payload(
            cls,
            user_code: str,
            token_max_age: Optional[int] = None,
        ) -> JWTPayloadViewModel:
            """
            获取 JWT Token Payload

            Args:
                user_code (str): 用户编号
                token_max_age (Optional[int]): Token 过期天数

            Returns:
                JWTPayloadViewModel: JWT Token Payload
            """

            issue_time: datetime = cls.get_now_time()
            expire_time: datetime = cls.get_expire_time(issue_time, token_max_age)

            payload: JWTPayloadViewModel = JWTPayloadViewModel(
                subject=cls.get_subject(),
                issue_time=TimeConverter.datetime_2_datetime_str(issue_time),
                expire_time=TimeConverter.datetime_2_datetime_str(expire_time),
                user_code=user_code,
            )

            return payload

    @staticmethod
    def get_secret() -> str:
        """ 获取 JWT 密钥 """

        return settings.SECURITY.SECRET_KEY

    @staticmethod
    def get_algorithm() -> str:
        """ 获取 JWT 加密算法 """

        return settings.SECURITY.ALGORITHM

    @staticmethod
    def create_or_update_token(
        key: str,
        issue_time: str,
        expire_time: str,
        user_code: str,
    ) -> None:
        """
        创建或更新 Token

        Args:
            key (str): 签发的 Token
            issue_time (str): 签发时间
            expire_time (str): 过期时间
            user_code (str): 用户编号
        """

        token_model: TokenDBModel = TokenDBModel(
            key=key,
            issue_time=issue_time,
            expire_time=expire_time,
            user_code=user_code,
        )
        query: dict = {"user_code": user_code}

        # 如果存在，则更新
        if token_db.token.count_documents(query):
            token_db.token.update_one(
                query,
                token_model.model_dump(),
            )

        # 如果不存在，则新增
        else:
            token_db.token.insert_one(token_model.model_dump())

    @classmethod
    def get_jwt(
        cls,
        user_code: str,
        token_max_age: Optional[int] = None,
    ) -> str:
        """
        获取 JWT Token

        Args:
            user_code (str): 用户编号
            token_max_age (Optional[int]): Token 过期天数

        Returns:
            str: JWT Token
        """

        # 1. 获取 Payload
        payload: JWTPayloadViewModel = cls.Payload.get_payload(user_code, token_max_age)

        # 2. 获取 JWT 密钥
        secret: str = cls.get_secret()

        # 3. 获取 JWT 加密算法
        algorithm: str = cls.get_algorithm()

        # 4. 生成 JWT Token
        jwt_string: str = jwt.encode(
            payload=payload.model_dump(),
            key=secret,
            algorithm=algorithm,
        )

        # 5. 创建或更新 Token
        cls.create_or_update_token(
            key=jwt_string,
            issue_time=payload.issue_time,
            expire_time=payload.expire_time,
            user_code=user_code,
        )

        return jwt_string

    @classmethod
    def get_payload_from_jwt(
        cls,
        token: str,
    ) -> JWTPayloadViewModel:
        """
        从 JWT Token 获取 Payload

        Args:
            token (str): Token

        Returns:
            str: JWT Token

        Raises:
            BusinessException: 非法 Token
        """

        secret: str = cls.get_secret()
        algorithm: str = cls.get_algorithm()

        try:
            payload_param: dict = jwt.decode(
                token=token,
                key=secret,
                algorithms=[algorithm],
            )
            payload: JWTPayloadViewModel = JWTPayloadViewModel(**payload_param)

        except Exception as e:
            raise BusinessException("非法 Token")

        return payload


if __name__ == '__main__':
    res = TokenHandler.get_jwt(user_code="12345678901234567890", token_max_age=7)
    debug(res)
